package de.alfenory.autobill.logic;

import de.alfenory.autobill.entity.*;
import de.alfenory.autobill.config.*;
import java.math.BigDecimal;
import java.util.*;
import java.util.logging.*;

import java.util.Set;

/**
 * Class for handling a booking using Facade Pattern. For creating the right booking entry there is need to know, if it is accounts receivable entry (=ar) or accounts payable entry (=ap).<br/>
 * Every booking consists a 
 * <ul>
 * <li>Booking</li>
 * <li>Vat Booking (if needed)</li>
 * <li>Counter Booking</li>
 * </ul>
 * The amount of booking is equals the sum of the amount of vat booking and the amount of counter booking.
 * 
 * @author alexander.fenske@alfenory.de
 * @see de.lxoffice.booking.Booking
 */
public class BookEntryFacade {

    private static Logger log = Logger.getLogger(BookEntryFacade.class.getName());
    
    public static double round(double value) {
        BigDecimal bigDecimal = new BigDecimal(value);
        return bigDecimal.setScale(3, BigDecimal.ROUND_HALF_UP).setScale(2, BigDecimal.ROUND_HALF_UP).doubleValue();
    }
    
    /**
     * Returns a booking object
     * @param chartId Id of the Account
     * @param value Value that was booked in the
     * @param date Date of the Accounting
     * @return @see de.lxoffice.accounting.Accounting
     */
    public static BookEntry getBookEntry(String account, String description, double value, Date date, int dimension[]) {
        BookEntry be = new BookEntry();
        be.setAccount(account);
        be.setDescription(description);
        be.setTransactionDate(date);
        be.setValue(value);
        if(dimension!=null) {
            for(int dimension1 : dimension) {
                be.getDimension().add(dimension1);
            }
        }
        return be;
    }

    public static BookEntryTemp getBookEntryTemp(Contact contact, OrderItem oi, Date transdate, List<Part> partList, List<TaxType> taxTypeList) {
        BookEntryTemp be = new BookEntryTemp();
        
        Part part = PartFacade.getPart(oi.getPartId(), partList);
        double vatValue = 0;
        if(part!=null) {
            be.setAccount(part.getAccount());
            if(part.getDimension()!=null) {
                for(int dim : part.getDimension()) {
                    be.getDimension().add(dim);
                }
            }
            if(!contact.isTaxFree()) {
                TaxType taxType = TaxTypeFacade.getTaxType(part.getTaxId(), taxTypeList);
                if(taxType!=null) {
                    be.setVatType(taxType.getAccount());
                    vatValue = TaxTypeFacade.getTaxValue(taxType, oi.getPrice()*oi.getQuantity(), contact);
                    be.setVatValue(vatValue);
                }
                else {
                    be.setVatType("");
                    be.setVatValue(0);
                }
            }
            else {
                be.setVatType("");
                be.setVatValue(0);
            }
        }
        else {
            be.setVatType("");
            be.setVatValue(0);
        }
        if(!contact.isTaxFree() && contact.isTaxIncluded()) {
            be.setValue(round(oi.getPrice()*oi.getQuantity()) - vatValue);
        }
        else {
            be.setValue(round(oi.getPrice()*oi.getQuantity()));
        }
        be.setDescription("/"+oi.getPartId());
        if(oi.getDate()!=null) {
            be.setTransactionDate(oi.getDate());
        }
        else {
            be.setTransactionDate(transdate);
        }
        return be;
    }
    
    /**
     * Creating Book Entries for oiList
     * @param contact
     * @param oiList
     * @param transdate
     * @param autobillConfiguration
     * @param partList list of alle parts (lookup tax type and more)
     * @param invoicenr number of the invoice
     * @return 
     */
    public static List<BookEntry> getBookEntryList(Contact contact, List<OrderItem> oiList, Date transdate, AutobillConfiguration autobillConfiguration, List<Part> partList, List<TaxType> taxTypeList, String invoicenr) {
        List<BookEntryTemp> betList = new ArrayList<BookEntryTemp>();
        
        //Creating book entries for every invoice position
        for(OrderItem oi : oiList) {
            betList.add(getBookEntryTemp(contact, oi, transdate, partList, taxTypeList));
        }
        
        //Flag for vatTypes and vatValues
        HashMap<String, Double> vatHashMap = new HashMap<String, Double>();
        double totalvalue = 0;
        Date date = null;
        for(BookEntryTemp bet : betList) {
            totalvalue += bet.getValue() + round(bet.getVatValue()*100.0)/100.0;
            if(!contact.isTaxFree()) {
                Double vat = vatHashMap.get(bet.getVatType());
                if(vat==null) {
                    vat = 0.0;
                }
                vat += bet.getVatValue();
                vatHashMap.put(bet.getVatType(), vat);
                date = bet.getTransactionDate();
            }
        }
        
        //Return List
        List<BookEntry> beList = new ArrayList<BookEntry>();
        
        //getting and Adding vat booking to beList
        if(!contact.isTaxFree()) {
            Iterator<String> vatTypes = vatHashMap.keySet().iterator();
            while(vatTypes.hasNext()) {
                String vatType = vatTypes.next();
                BookEntry be = new BookEntry();
                be.setAccount(vatType);
                if(vatHashMap.get(vatType)!=null) {
                    be.setValue(round(vatHashMap.get(vatType)));
                }
                be.setDescription(autobillConfiguration.getVatText()+"/"+invoicenr);
                be.setTransactionDate(transdate);
                beList.add(be);
            }
        }
        
        totalvalue = round(totalvalue);
        
        if(date==null) {
            date = transdate;
        }
        
        //Add Cleared Book Entry List
        beList.addAll(clearBookEntryList(betList));
        
        //counter booking
        beList.add(getBookEntry(contact.getAccount(),"/"+invoicenr, totalvalue*-1, date, null));
        
        return beList;
    }
    
    /**
     * Clears the book entry list by removing double occured book entry having the same account.
     * @param betList List to be cleared
     * @return Cleared list
     */
    public static List<BookEntry> clearBookEntryList(List<BookEntryTemp> betList) {
        
        for(int i=0;i<betList.size();i++) {
            for(int j=i+1;j<betList.size();j++) {
                if(betList.get(i).getAccount().equals(betList.get(j).getAccount()) && betList.get(i).getDimension().equals(betList.get(j).getDimension())) {
                    BookEntryTemp bet1 = betList.get(i);
                    BookEntryTemp bet2 = betList.get(j);
                    bet1.setDescription(bet1.getDescription()+bet2.getDescription());
                    bet1.setValue(bet1.getValue()+bet2.getValue());
                    betList.set(i, bet1);
                    betList.remove(j);
                    j--;
                }
            }
        }
        
        List<BookEntry> beList = new ArrayList<BookEntry>();
        for(BookEntryTemp bet : betList) {
            bet.setValue(round(bet.getValue()));
            beList.add(bet.toBookEntry());
        }
        return beList;
    }
    
    public static double getTotalValue(List<BookEntry> beList, Contact contact) {
        for(BookEntry be : beList) {
            if(be.getAccount()!=null && contact.getAccount()!=null) {
                if(be.getAccount().equals(contact.getAccount())) {
                    return be.getValue()*-1;
                }
            }
        }
        return 0;
    }
    
    /**
     * 
     * @param beList
     * @param ttList
     * @return 
     */
    public static double getVatValue(List<BookEntry> beList, List<TaxType> ttList) {
        double vatValue = 0;
        if(beList==null || ttList==null) {
            return 0.0;
        }
        for(BookEntry be : beList) {
            for(TaxType tt : ttList) {
                if(be.getAccount()!=null && tt.getAccount()!=null) {
                    if(be.getAccount().equals(tt.getAccount())) {
                        vatValue += be.getValue();
                    }
                }
            }
        }
        return vatValue;
    }
}